package com.boost.netty.buf.sbytes;

import java.util.Arrays;

public class StringBuilderPro {

    private int writerIndex;
    private byte[] content;
    private Filed headFiled;
    private Filed tailFiled;

    public StringBuilderPro() {
        content = new byte[0];
    }

    public StringBuilderPro(int capacity) {
        content = new byte[capacity];
    }

    public StringBuilderPro append(String filedName, String value) {
        Filed filed = new Filed();
        filed.setName(filedName);
        filed.setBegin(writerIndex);
        append(value);
        filed.setEnd(writerIndex);
        if (headFiled == null) {
            headFiled = tailFiled = filed;
            return this;
        }
        tailFiled.setNext(filed);
        tailFiled = filed;
        return this;
    }

    public StringBuilderPro append(String value) {
        byte[] bytes = value.getBytes();
        int length = bytes.length;
        int i = writerIndex + length;
        if (i > content.length) { // 扩容
            content = Arrays.copyOf(content, (content.length << 1) + length);
        }
        System.arraycopy(bytes, 0, content, writerIndex, length);
        writerIndex += length;
        return this;
    }

    public String getValueByFiledName(String filedName) {
        return getValue(findFiled(filedName));
    }

    public String getValue(Filed filed) {
        byte[] bytes = new byte[filed.getEnd() - filed.getBegin()];
        System.arraycopy(content, filed.getBegin(), bytes, 0, bytes.length);
        return new String(bytes);
    }


    public Filed findFiled(String filedName) {
        Filed filed = headFiled;
        while (filed != null) {
            if (filedName.equals(filed.getName())) {
                return filed;
            }
            filed = filed.getNext();
        }
        return null;
    }

    public StringBuilderPro insertHead(String str) {
        byte[] bytes = str.getBytes();
        int length = bytes.length;
        ensureCapacityInternal(length);
        shift(0, length);
        //替换
        System.arraycopy(bytes, 0, content, 0, length);
        writerIndex += length;
        correct(headFiled, length);
        return this;
    }

    public boolean isEmpty() {
        return writerIndex < 1 || content.length < 1;
    }


    public StringBuilderPro replace(String filedName, String newValue) throws Exception {
        return replace(findFiled(filedName),newValue);
    }


    public StringBuilderPro replace(Filed filed, String newValue) throws Exception {
        byte[] bytes = newValue.getBytes();
        int begin = filed.getBegin();
        int newEnd = begin + bytes.length;
        int oldEnd = filed.getEnd();
        int difference = newEnd - oldEnd;
        //扩容
        ensureCapacityInternal(difference);
        //挪动数组
        shift(oldEnd, newEnd);
        //替换
        System.arraycopy(bytes, 0, content, begin, bytes.length);
        writerIndex += difference;
        //修正位置
        filed.setEnd(oldEnd + difference);
        correct(filed.getNext(), difference);
        return this;
    }


    private void correct(Filed filed, int difference) {
        while (filed != null) {
            filed.setBegin(filed.getBegin() + difference);
            filed.setEnd(filed.getEnd() + difference);
            filed = filed.getNext();
        }
    }

    private void shift(int offset, int n) {
        //挪动数组
        System.arraycopy(content, offset, content, n, writerIndex - offset);
    }


    private void ensureCapacityInternal(int difference) {
        int newCount = writerIndex + difference;
        if (newCount > content.length) {
            //扩容
            content = Arrays.copyOf(content, (content.length << 1) + difference);
        }
    }

    public boolean containsFiled(String filedName) {
        if (headFiled == null) return false;
        Filed filed = headFiled;
        while (filed != null) {
            if (filedName.equals(filed.getName())) {
                return true;
            }
            filed = filed.getNext();
        }
        return false;
    }


    public int getWriterIndex() {
        return writerIndex;
    }

    public void setWriterIndex(int writerIndex) {
        this.writerIndex = writerIndex;
    }

    public byte[] getContent() {
        return content;
    }

    public void setContent(byte[] content) {
        this.content = content;
    }

    public Filed getHeadFiled() {
        return headFiled;
    }

    public void setHeadFiled(Filed headFiled) {
        this.headFiled = headFiled;
    }
}
